package demo;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiAttendanceListRecordRequest;
import com.dingtalk.api.response.OapiAttendanceListRecordResponse;
import com.taobao.api.ApiException;
import demo.entity.vo.UserInfoAndTimeVo;
import demo.service.OutputService;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static java.util.Comparator.comparingLong;

/**
 * Created By  醉美柳舞之众星捧月
 *
 * @author song
 * @date 2021/4/28 11:20
 */
public class MouseClickSelectFileEnter extends JFrame implements MouseListener {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Override
    public void mouseClicked(MouseEvent e) {

        JFileChooser chooser = new JFileChooser();
        chooser.setMultiSelectionEnabled(true);
        int returnVal = chooser.showOpenDialog(e.getComponent());
        System.out.println("returnVal=" + returnVal);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            String filepath = chooser.getSelectedFile().getAbsolutePath();
            if (StrUtil.isBlank(filepath)) {
                JOptionPane.showMessageDialog(null, "请先选择文件~", "警告", JOptionPane.WARNING_MESSAGE);
                return;
            }

            String suffix = filepath.substring(filepath.lastIndexOf(".") + 1);
            System.out.println(suffix);
            if (!("xlsx".equals(suffix) || ("xls".equals(suffix)))) {
                JOptionPane.showMessageDialog(null, "请选择Excel文件~", "警告", JOptionPane.WARNING_MESSAGE);
                return;
            }
            try {
                openFile(filepath);
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }
    }

    private void openFile(String filepath) throws IOException, ApiException {
        FileInputStream fileInputStream = new FileInputStream(filepath);
        XSSFWorkbook workbook = new XSSFWorkbook(fileInputStream);
        Map<Date, List<UserInfoAndTimeVo>> parsing = parsing(queryRows(workbook));
        try {
            createRsExcel(new TreeMap<>(parsing));
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    public List<Row> queryRows(XSSFWorkbook workbook) {
        List<Row> list = new ArrayList<>();
        XSSFSheet sheet1 = workbook.getSheetAt(0);
        for (int i = 1; i <= sheet1.getLastRowNum(); i++) {
            if (sheet1.getRow(i).getCell(2).getStringCellValue().contains("完成")) {
                list.add(sheet1.getRow(i));
            }
        }
        try {
            XSSFSheet sheet2 = workbook.getSheetAt(1);
            for (int i = 1; i <= sheet2.getLastRowNum(); i++) {
                if (sheet2.getRow(i).getCell(2).getStringCellValue().contains("完成")) {
                    list.add(sheet2.getRow(i));
                }
            }
        } catch (Exception exception) {
            log.error("第二个Sheet不存在数据");
        }
        try {
            XSSFSheet sheet3 = workbook.getSheetAt(2);
            for (int i = 1; i <= sheet3.getLastRowNum(); i++) {
                if (sheet3.getRow(i).getCell(2).getStringCellValue().contains("完成")) {
                    list.add(sheet3.getRow(i));
                }
            }
        } catch (Exception exception) {
            log.error("第三个Sheet不存在数据");
        }
        return list;
    }

    private static void setCellGBKValue(Cell cell, String value) {
        cell.setCellType(CellType.STRING);
        cell.setCellValue(value);
    }

    private void setInfantCellTitle(HSSFSheet sheet) {
        HSSFRow row = sheet.createRow(0);
        setCellGBKValue(row.createCell((short) 0), "加班时间");
        setCellGBKValue(row.createCell((short) 1), "用户ID");
        setCellGBKValue(row.createCell((short) 2), "部门ID");
        setCellGBKValue(row.createCell((short) 3), "用户名称");
        setCellGBKValue(row.createCell((short) 4), "week Day");
        setCellGBKValue(row.createCell((short) 5), "上班打卡时间");
        setCellGBKValue(row.createCell((short) 6), "下班打卡时间");
        setCellGBKValue(row.createCell((short) 7), "加班时差");
        setCellGBKValue(row.createCell((short) 8), "若是周末加班");
    }

    private void setSheet2(HSSFSheet sheet) {
        HSSFRow row = sheet.createRow(0);
        setCellGBKValue(row.createCell((short) 0), "姓名");
        setCellGBKValue(row.createCell((short) 1), "周一到周五加班总和");
        setCellGBKValue(row.createCell((short) 2), "周末加班算上下班");
        setCellGBKValue(row.createCell((short) 3), "周末加班算整天加班");

    }

    private static final List<Integer> WEEK_END = Arrays.asList(6, 7);
    private static final List<String> NEED_EXCLUDE = Arrays.asList(
            "张鹏",
            "张贝佳",
            "汪文旭",
            "施丽雯",
            "张菲倪",
            "廖斯琪",
            "左欢欢",
            "胡翼",
            "田甜",
            "唐小晨",
            "蒲丹");

    private void createRsExcel(Map<Date, List<UserInfoAndTimeVo>> parsing) throws Exception {

        HSSFWorkbook workBook = new HSSFWorkbook();
        List<UserInfoAndTimeVo> singlePeople = new ArrayList<>();
        try {
            HSSFSheet sheet = workBook.createSheet("Sheet1");
            setInfantCellTitle(sheet);

            int b = 0;

            for (Map.Entry<Date, List<UserInfoAndTimeVo>> entry : parsing.entrySet()) {
                List<UserInfoAndTimeVo> value = entry.getValue();
                singlePeople.addAll(value);
                for (int p = 1; p <= value.size(); p++) {
                    HSSFRow row = sheet.createRow(p + b);
                    UserInfoAndTimeVo userInfoAndTimeVo = value.get(p - 1);
                    Class cls = userInfoAndTimeVo.getClass();
                    Field[] fields = cls.getDeclaredFields();
                    for (int k = 0; k < fields.length; k++) {
                        Field f = fields[k];
                        f.setAccessible(true);
                        try {
                            Object o = f.get(userInfoAndTimeVo);
                            if (o instanceof Date) {
                                if (k == 0) {
                                    row.createCell((short) k).setCellValue(DateUtil.format((Date) o, "yyyy/MM/dd"));
                                } else {
                                    row.createCell((short) k).setCellValue(DateUtil.formatDateTime((Date) o));
                                }
                            } else {
                                row.createCell((short) k).setCellValue(null == o ? "" : String.valueOf(o));
                            }
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
                b = b + value.size();
            }

            HSSFSheet sheet2 = workBook.createSheet("Sheet2");
            setSheet2(sheet2);
            Map<String, List<UserInfoAndTimeVo>> collect = singlePeople.stream().collect(Collectors.groupingBy(UserInfoAndTimeVo::getUserName));
            int i = 1;
            for (Map.Entry<String, List<UserInfoAndTimeVo>> entry : collect.entrySet()) {
                String key = entry.getKey();
                List<UserInfoAndTimeVo> value = entry.getValue();

                int allSum = (int) Math.floor(value.stream().filter(p -> null != p.getDiffTime()).mapToDouble(UserInfoAndTimeVo::getDiffTime).sum());

                int weekEndBigTime = (int) Math.floor(value.stream().filter(p -> null != p.getWeekDiffTime()).mapToDouble(UserInfoAndTimeVo::getWeekDiffTime).sum());

                List<UserInfoAndTimeVo> weekEndList = value.stream().filter(p -> WEEK_END.contains(Integer.valueOf(p.getWeekDay().substring(1, 2)))).collect(Collectors.toList());

                int weekEndTime = (int) Math.floor(weekEndList.stream().filter(p -> null != p.getDiffTime()).mapToDouble(UserInfoAndTimeVo::getDiffTime).sum());

                HSSFRow row = sheet2.createRow(i);
                row.createCell((short) 0).setCellValue(key);
                row.createCell((short) 1).setCellValue(allSum - weekEndTime);
                row.createCell((short) 2).setCellValue(weekEndTime);
                row.createCell((short) 3).setCellValue(weekEndBigTime);
                i++;
            }

            FileDialog fd = new FileDialog(this, "导出考勤加班结果集", FileDialog.SAVE);
            fd.setLocation(500, 350);
            fd.setVisible(true);
            String stringFile = fd.getDirectory() + fd.getFile() + ".xls";

            FileOutputStream fOut = new FileOutputStream(stringFile);
            workBook.write(fOut);
            fOut.flush();
            fOut.close();
            JOptionPane.showMessageDialog(null, "导出成功！");
        } catch (java.io.FileNotFoundException ex) {
            JOptionPane.showMessageDialog(null, "所要导出的Excel文件被占用，请关闭该文件并重试此操作！");
        }
    }

    private String changeChar(String s) {
        String result = ",";
        s = s.replaceAll(" ", result);
        s = s.replaceAll("、", result);
        s = s.replaceAll("，", result);
        s = s.replaceAll(",,", result);
        s = s.replaceAll("，", result);
        s = s.replaceAll("\\ ", result);
        s = s.replaceAll("[ ]", result);
        s = s.replaceAll("[，]", result);
        s = s.replaceAll("\\，", result);
        s = s.replaceAll("[\\ ]", result);
        s = s.replaceAll("[\\，]", result);
        s = s.trim();
        return s;
    }

    private Map<Date, List<UserInfoAndTimeVo>> parsing(List<Row> rowList) throws ApiException {
        if (CollectionUtil.isEmpty(rowList)) {
            JOptionPane.showMessageDialog(null, "不存在有效数据!");
        }
        Map<String, String> objectObjectHashMap = new HashMap<>(20);
        rowList.forEach(i -> {
            String stringCellValue16 = i.getCell(16).getStringCellValue();
            System.out.println(stringCellValue16);

            String stringCellValue19 = i.getCell(19).getStringCellValue();
            System.out.println(stringCellValue19);

            if (StrUtil.isNotBlank(stringCellValue16) && StrUtil.isNotBlank(stringCellValue19)) {
                if (objectObjectHashMap.containsKey(stringCellValue19)) {
                    String s = objectObjectHashMap.get(stringCellValue19);
                    s = s + "," + stringCellValue16;
                    objectObjectHashMap.put(stringCellValue19, changeChar(s));
                } else {
                    objectObjectHashMap.put(stringCellValue19, changeChar(stringCellValue16));
                }
            }
        });
        if (CollectionUtil.isEmpty(objectObjectHashMap)) {
            JOptionPane.showMessageDialog(null, "人数或时间缺失!");
        }
        Map<String, List<String>> timeAndPeople = new HashMap<>(objectObjectHashMap.size());
        objectObjectHashMap.forEach((i, j) -> timeAndPeople.put(i, Arrays.stream(j.split(",")).distinct().collect(Collectors.toList())));
        OutputService outputService = new OutputService();
        List<String> userDepotAndUserIdAndUserName = outputService.getUserDepotAndUserIdAndUserName();
        List<UserInfoAndTimeVo> objects = new ArrayList<>();
        timeAndPeople.forEach((i, j) -> j.forEach(p -> userDepotAndUserIdAndUserName.forEach(k -> {
            String name = k.split("_")[2];
            if (p.equalsIgnoreCase(name) && !NEED_EXCLUDE.contains(name)) {
                UserInfoAndTimeVo userInfoAndTimeVo = new UserInfoAndTimeVo();
                userInfoAndTimeVo.setUserId(k.split("_")[1]);
                userInfoAndTimeVo.setDepotId(k.split("_")[0]);
                userInfoAndTimeVo.setUserName(k.split("_")[2]);
                userInfoAndTimeVo.setTime(DateUtil.beginOfDay(DateUtil.parse(i)));
                Integer integer = dateToWeek(i);
                if (WEEK_END.contains(integer)) {
                    userInfoAndTimeVo.setWeekDay("周" + integer + "; Manual ");
                } else {
                    userInfoAndTimeVo.setWeekDay("周" + integer);
                }
                objects.add(userInfoAndTimeVo);
            }
        })));
        Map<Date, List<UserInfoAndTimeVo>> collect = objects.stream().collect(Collectors.groupingBy(UserInfoAndTimeVo::getTime));
        getKaResult(collect);
        differenceTime(collect);
        return collect;
    }

    public Integer dateToWeek(String datetime) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Integer[] weekDays = {7, 1, 2, 3, 4, 5, 6};
        Calendar cal = Calendar.getInstance();
        Date date;
        try {
            date = sdf.parse(datetime);
            cal.setTime(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        return weekDays[w];
    }

    private void getKaResult(Map<Date, List<UserInfoAndTimeVo>> collect) {
        collect.forEach((i, j) -> j.forEach(p -> {
            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/attendance/listRecord");
            OapiAttendanceListRecordRequest req = new OapiAttendanceListRecordRequest();
            req.setUserIds(Collections.singletonList(p.getUserId()));
            req.setCheckDateFrom(DateUtil.formatDateTime(i));
            req.setCheckDateTo(DateUtil.formatDateTime(DateUtil.offset(i, DateField.HOUR, 30)));
            req.setIsI18n(false);
            OapiAttendanceListRecordResponse rsp = null;
            try {
                rsp = client.execute(req, new OutputService().getAccessToken());
            } catch (ApiException e) {
                JOptionPane.showMessageDialog(null, "网络错误-重试吧重试吧~~~" + e.getMessage(), "重试吧重试吧~~~", JOptionPane.WARNING_MESSAGE);
            }

            try {
                List<OapiAttendanceListRecordResponse.Recordresult> recordResult = Objects.requireNonNull(rsp).getRecordresult();
                List<OapiAttendanceListRecordResponse.Recordresult> recordResults = recordResult.stream().filter(y -> StrUtil.isNotBlank(y.getCheckType())).collect(Collectors.toList());
                Map<String, List<OapiAttendanceListRecordResponse.Recordresult>> listMap = recordResults.stream().collect(Collectors.groupingBy(OapiAttendanceListRecordResponse.Recordresult::getCheckType));

                AtomicReference<Date> upTime = new AtomicReference<>();
                AtomicReference<Date> offTime = new AtomicReference<>();

                listMap.forEach((g, h) -> {
                    if ("OnDuty".equalsIgnoreCase(g)) {
                        if (h.size() > 1) {
                            Optional<OapiAttendanceListRecordResponse.Recordresult> min = h.stream().min(comparingLong(t -> t.getUserCheckTime().getTime()));
                            min.ifPresent(recordResultMin -> upTime.set(recordResultMin.getUserCheckTime()));
                        } else {
                            upTime.set(h.get(0).getUserCheckTime());
                        }
                    }
                    if ("OffDuty".equalsIgnoreCase(g)) {
                        if (h.size() > 1) {
                            Optional<OapiAttendanceListRecordResponse.Recordresult> max = h.stream().max(comparingLong(t -> t.getUserCheckTime().getTime()));
                            max.ifPresent(recordResultMax -> offTime.set(recordResultMax.getUserCheckTime()));
                        } else {
                            offTime.set(h.get(0).getUserCheckTime());
                        }
                    }
                });
                p.setOnTime(upTime.get());
                p.setOffTime(offTime.get());
            } catch (Exception exception) {
                log.error("用户[{}]打卡异常", p.getUserName());
            }
        }));
    }

    private void differenceTime(Map<Date, List<UserInfoAndTimeVo>> collect) {
        collect.forEach((i, j) -> j.stream().filter(k -> null != k.getOnTime() && null != k.getOffTime()).forEach(p -> {
            Date onTime = p.getOnTime();
            Date offTime = p.getOffTime();

            DateTime begin = DateUtil.beginOfDay(onTime);
            Date nine = DateUtil.offset(begin, DateField.HOUR, 9);
            Date ten = DateUtil.offset(begin, DateField.HOUR, 10);
            Date twelve = DateUtil.offset(begin, DateField.HOUR, 12);
            Date off = DateUtil.offset(begin, DateField.HOUR, 18);
            Date off1830 = DateUtil.offset(off, DateField.MINUTE, 30);

            Integer integer = dateToWeek(DateUtil.formatDateTime(begin));
            if (WEEK_END.contains(integer)) {
                long min = DateUtil.between(onTime, offTime, DateUnit.MINUTE);
                p.setWeekDiffTime(BigDecimal.valueOf((float) min / 60).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
            }

            if (offTime.before(off)) {
                p.setDiffTime((double) 0);
            } else {
                if (onTime.after(begin) && onTime.getTime() <= nine.getTime()) {
                    /* TODO  直接计算时间 */
                    long min = DateUtil.between(off1830, offTime, DateUnit.MINUTE);
                    p.setDiffTime(BigDecimal.valueOf((float) min / 60).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
                }
                if (onTime.after(nine) && onTime.getTime() <= ten.getTime()) {
                    /* TODO 需要减去时差 */
                    long min = DateUtil.between(nine, onTime, DateUnit.MINUTE);
                    DateTime shouldBeAddWork = DateUtil.offset(off1830, DateField.MINUTE, (int) min);
                    long min233 = DateUtil.between(shouldBeAddWork, offTime, DateUnit.MINUTE);
                    p.setDiffTime(BigDecimal.valueOf((float) min233 / 60).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
                }
                if (onTime.after(twelve)) {
                    /* TODO  下午打 上班卡;可能是上午调休；所以从6点半开始算时间 */
                    long min = DateUtil.between(off1830, offTime, DateUnit.MINUTE);
                    p.setDiffTime(BigDecimal.valueOf((float) min / 60).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
                    p.setWeekDay(p.getWeekDay() + "; 半天");
                }
            }
        }));
        collect.forEach((i, j) -> j.stream().filter(k -> null == k.getOnTime() || null == k.getOffTime()).forEach(p -> {
            /* TODO 此处就不管周末了 */
            if (null == p.getOnTime() && null != p.getOffTime()) {
                Date offTime = p.getOffTime();
                Date off = DateUtil.offset(DateUtil.beginOfDay(offTime), DateField.HOUR, 18);
                Date off1830 = DateUtil.offset(off, DateField.MINUTE, 30);
                if (offTime.after(off1830)) {
                    long min = DateUtil.between(off1830, offTime, DateUnit.MINUTE);
                    p.setDiffTime(BigDecimal.valueOf((float) min / 60).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
                    p.setWeekDay(p.getWeekDay() + "; 默认上午请假");
                }
            }
            if (null != p.getOnTime() && null == p.getOffTime()) {
                p.setDiffTime((double) 0);
                p.setWeekDay(p.getWeekDay() + "; 默认补卡18点多");
            }
        }));
    }

    @Override
    public void mousePressed(MouseEvent e) {

    }

    @Override
    public void mouseReleased(MouseEvent e) {

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }
}
